
#include <BLabel>
#include <BStyle>
#include <BHBoxLayout>
#include <BVBoxLayout>
#include <BGridLayout>
#include <BScrollBar>
#include <BCursor>

#include "PropertyBox.h"

PropertyBox::PropertyBox(BWidget* parent) : BGroupBox(parent)
{
	this->setWired(false);
	this->setTitleAlign(Align_LeftCenter);

	BGridLayout* nameLayout = new BGridLayout();
	nameLayout->setRowSpacing(2);
	nameLayout->setColumnSpacing(2);
	nameLayout->setColumnPolicy(0, Policy_Preferred | Policy_Expanding);
	{
		BLabel* nameLabel = new BLabel("name");
		_nameComboBox = new BComboBox();
		_nameComboBox->setEditable(true);
		if (BWidget* board = _nameComboBox->child("board"))
		{
			board->setPerch(Part_Left, 20);
			connect(board, Signal_Framed, &PropertyBox::slotNameBoardFramed);
		}

		BLabel* formatLabel = new BLabel("format");
		_formatEdit = new BLineEdit();

		nameLayout->addWidget(0, 0, nameLabel);
		nameLayout->addWidget(0, 1, _nameComboBox);
		nameLayout->addWidget(1, 0, formatLabel);
		nameLayout->addWidget(1, 1, _formatEdit);
	}

	BHBoxLayout* attribLayout = new BHBoxLayout();
	{
		_insertRowButton = new BButton("Insert");
		_insertRowButton->setForm(Form_IconAlone);

		_removeRowButton = new BButton("Remove");
		_removeRowButton->setForm(Form_IconAlone);

		attribLayout->addWidget(new BLabel("Attribs"));
		attribLayout->addWidget(_insertRowButton, 0);
		attribLayout->addWidget(_removeRowButton, 0);
	}

	_attribTable = new BTableWidget();
	_attribTable->setColumnCount(2);
	_attribTable->setColumnLabel(0, "name");
	_attribTable->setColumnLabel(1, "value");
	_attribTable->setGridStyle(Grid_Crossing);
	_attribTable->verticalScrollBar()->setFixedWidth(2);
	_attribTable->verticalScrollBar()->setWidthPolicy(Policy_Fixed);
	_attribTable->setColumnPolicy(Policy_Preferred | Policy_Expanding);
	_attribTable->setAreaPolicy(Policy_Preferred | Policy_Expanding, Policy_Preferred);
	_attribTable->setSelectMode(Select_Rows | Select_Single);
	{
		_attribEdit = new BLineEdit(_attribTable);
		_attribEdit->setVisible(false);

		_editButton = new BButton(_attribTable);
		_editButton->setName("edit");
		_editButton->setVisible(false);
	}

	_treeWidget = new BTreeWidget();
	{
		BHBoxLayout* titleLayout = new BHBoxLayout(_treeWidget, Part_Top);
		titleLayout->addWidget(new BLabel("Hierarchies"));
		_treeMenu = new BMenu(_treeWidget);
		_treeMenu->addAction("add");
		_treeMenu->addAction("prepend");
		_treeMenu->addAction("append");
		_treeMenu->addAction("delete");
	}

	BVBoxLayout* vlayout = new BVBoxLayout(this);
	vlayout->setSpacing(3);
	vlayout->addLayout(nameLayout, 0);
	vlayout->addLayout(attribLayout, 0);
	vlayout->addWidget(_attribTable, 0);
	vlayout->addWidget(_treeWidget);

	connect(_nameComboBox, Signal_EditFinished, &PropertyBox::slotNameEditFinished);
	connect(_nameComboBox, Signal_ItemClicked, &PropertyBox::slotNameItemClicked);
	connect(_formatEdit, Signal_EditFinished, &PropertyBox::slotFormatEditFinished);
	connect(_attribEdit, Signal_EditFinished, &PropertyBox::slotAttribEditFinished);
	connect(_attribEdit, Signal_Focused, &PropertyBox::slotAttribEditFocused);
	connect(_editButton, Signal_Clicked, &PropertyBox::slotEditButtonClicked);

	connect(_insertRowButton, Signal_Clicked, &PropertyBox::slotInsertRowButtonClicked);
	connect(_removeRowButton, Signal_Clicked, &PropertyBox::slotRemoveRowButtonClicked);
	connect(_treeWidget, Signal_ItemSelected, &PropertyBox::slotTreeItemSelected);
	connect(_treeWidget, Signal_ItemRightClicked, &PropertyBox::slotTreeItemRightClicked);
	connect(_treeMenu, Signal_Triggered, &PropertyBox::slotTreeMenuTriggered);

	attachHtmlWidget(0);
}
PropertyBox::~PropertyBox()
{

}

void PropertyBox::attachHtmlWidget(BHtmlWidget* htmlWidget)
{
	if (_htmlWidget != htmlWidget)
	{
		_nameComboBox->clearItems();
		_attribTable->clearItems();
		_formatEdit->setText(BString());
		if (_htmlWidget)
		{
			disconnect(_htmlWidget);
			this->setTitle(BString());
		}
		_htmlWidget = htmlWidget;
		if (_htmlWidget)
		{
			connect(_htmlWidget, Signal_ItemSelected, &PropertyBox::slotHtmlItemSelected);
			connect(_htmlWidget, Signal_ItemChanged, &PropertyBox::slotHtmlItemChanged);
			connect(_htmlWidget, Signal_ReadOnly, &PropertyBox::slotHtmlWidgetReadOnly);

			resetTreeWidget(_htmlWidget->html());
		}
	}
	this->layout()->setEnabled(_htmlWidget);
	updateEditable();
}

BSize PropertyBox::sizeHint() const
{
	BSize hsize = BGroupBox::sizeHint();
	return hsize;
}

void PropertyBox::resetTreeWidget(BXmlNode* xmlNode)
{
	_treeWidget->clearItems();
	BTreeItem* item = createTreeItem(xmlNode);
	freshTreeItemIcon(item);
	_treeWidget->addItem(item);
}
BTreeItem* PropertyBox::createTreeItem(BXmlNode* xmlNode)
{
	const BString& name = xmlNode->name();
	BTreeItem* treeItem = new BTreeItem(xmlNode->name());
	treeItem->setAsset(xmlNode);
	for (int i = 0; i < xmlNode->childCount(); i++)
	{
		BXmlNode* child = xmlNode->child(i);
		BTreeItem* childItem = createTreeItem(child);
		treeItem->addChild(childItem);
	}
	return treeItem;
}
void PropertyBox::freshTreeItemIcon(BTreeItem* item)
{
	const BStyle* style = this->realStyle();
	if (item->text().empty())
	{
		BIcon* icon = style->icon("text");
		item->setIcon(icon);
	}
	else
	{
		BIcon* icon = style->icon(item->text());
		item->setIcon(icon);
	}
	for (int i = 0; i < item->childCount(); i++)
	{
		BTreeItem* child = item->child(i);
		freshTreeItemIcon(child);
	}
}
void PropertyBox::updateEditable()
{
	bool editable = _htmlWidget && _htmlWidget->editable();
	_nameComboBox->setEditable(editable);
	_formatEdit->setReadOnly(!editable);
	_insertRowButton->setEnabled(editable);
	_removeRowButton->setEnabled(editable);
	if (editable)
	{
		connect(_attribTable, Signal_DoubleClicked, &PropertyBox::slotAttribItemDoubleClicked);
		connect(_attribTable, Signal_ItemSelected, &PropertyBox::slotAttribItemSelected);
		connect(_attribTable, Signal_Freshed, &PropertyBox::slotAttribTableFreshed);
	}
	else
	{
		disconnect(_attribTable, Signal_DoubleClicked);
	}
}
void PropertyBox::adjustEditButton()
{
	if (BTableItem* item = _attribTable->selectedItem())
	{
		int row = item->row();
		BRect rowRect = _attribTable->rowRect(row);
		BRect rect = rowRect.align(Align_RightCenter, _editButton->sizeHint());
		_editButton->setRect(rect);
	}
}

void PropertyBox::slotHtmlWidgetReadOnly(BObject* object, const BValue& value)
{
	updateEditable();
}
void PropertyBox::slotHtmlItemSelected(BObject* object, const BValue& value)
{
	if (BHtmlItem* htmlItem = value)
	{
		BTreeItem* item = _treeWidget->itemOfAsset(value);
		_treeWidget->clearSelection();
		_treeWidget->setItemSelected(item, htmlItem->selected());
		_treeWidget->scrollToItem(item);
	}
}
void PropertyBox::slotHtmlItemChanged(BObject* object, const BValue& value)
{
	BHtmlItem* htmlItem = value;
	BTreeItem* item = _treeWidget->itemOfAsset(htmlItem);
	if (item)
	{
		item->clearChildren();
		for (int i = 0; i < htmlItem->childCount(); i++)
		{
			BHtmlItem* childItem = htmlItem->child(i);
			BTreeItem* child = createTreeItem(childItem);
			freshTreeItemIcon(child);
			item->addChild(child);
		}
	}
}
void PropertyBox::slotHtmlItemTextChanged(BObject* object, const BValue& value)
{
	if (_htmlNode != object)
		return;
}
void PropertyBox::slotNameEditFinished(BObject* object, const BValue& value)
{
	const BString& name = _nameComboBox->text();
	_htmlNode->setName(name);
}
void PropertyBox::slotNameBoardFramed(BWidget* widget, const BValue& value)
{
	BPainter painter(widget);
	BRect leftRect = widget->rect(Part_Left);
	for (int i = 0; i < _nameComboBox->itemCount(); i++)
	{
		BComboItem* item = _nameComboBox->item(i);
		BXmlNode* node = item->value();
		int level = node->level();
		BRect rect = item->rect();
		rect.left() = leftRect.left();
		rect.right() = leftRect.right();
		painter.setColor(36, 72, 108);
		painter.drawText(rect, level, Align_Center);
	}
}
void PropertyBox::slotNameItemClicked(BObject* object, const BValue& value)
{
	BHtmlItem* item = _nameComboBox->value();
	_htmlWidget->setItemSelected(item);
}
void PropertyBox::slotFormatEditFinished(BObject* object, const BValue& value)
{
	BString format = value;
	format.replace("\\n", "\n");
	if (_htmlNode)
	{
		_htmlNode->setFormat(format);
		this->emit(Signal_ItemEdited, _htmlNode);
	}
}
void PropertyBox::slotAttribItemDoubleClicked(BObject* object, const BValue& value)
{
	const BPoint& pos = value;
	BTableItem* item = _attribTable->itemAt(pos);
	if (!item)
	{
		int row = _attribTable->rowAt(pos.y());
		int column = _attribTable->columnAt(pos.x());
		if (_attribTable->checkCell(row, column))
		{
			item = new BTableItem();
			_attribTable->setItem(row, column, item);
			item->setSelected(true);
		}
	}
	if (item)
	{
		BRect rect = _attribTable->cellRect(item->row(), item->column());
		_attribEdit->setRect(rect);
		_attribEdit->setText(item->text());
		_attribEdit->popup();
		_attribEdit->setFocused(true);
		_attribEdit->setAsset(item);
	}
}
void PropertyBox::slotAttribItemSelected(BObject* object, const BValue& value)
{
	BTableItem* item = value;
	if (item->selected())
	{
		if (item->text() == "href")
		{
			BTableItem* fileItem = _attribTable->item(item->row(), 1);
			if (fileItem->text().size())
			{
				BString fileName = fileItem->text();
				fileName.prepend('/');
				fileName.prepend(_htmlWidget->fileName().path());
				_editButton->setAsset(fileName);
				_editButton->show();
				adjustEditButton();
			}
		}
	}
	else
	{
		_editButton->setAsset(BValue());
		_editButton->hide();
	}
	updateEditable();
}
void PropertyBox::slotAttribTableFreshed(BObject* object, const BValue& value)
{
	adjustEditButton();
}
void PropertyBox::slotAttribEditFinished(BObject* object, const BValue& value)
{
	_attribEdit->hide();
	BTableItem* item = _attribEdit->asset();
	item->setText(_attribEdit->text());
	int row = item->row();
	BTableItem* nameItem = _attribTable->item(row, 0);
	BTableItem* valueItem = _attribTable->item(row, 1);
	if (_htmlNode && nameItem && valueItem)
	{
		_htmlNode->setAttrib(nameItem->text(), valueItem->text());
		this->emit(Signal_ItemEdited, _htmlNode);
	}
	updateEditable();
}
void PropertyBox::slotAttribEditFocused(BObject* object, const BValue& value)
{
	if (value == false)
	{
		_attribEdit->hide();
	}
}

void PropertyBox::slotInsertRowButtonClicked(BObject* object, const BValue& value)
{
	int row = _attribTable->rowCount();
	if (BTableItem* item = _attribTable->selectedItem())
	{
		row = item->row();
	}
	_attribTable->insertRows(row, 1);
	this->emit(Signal_ItemEdited, _htmlNode);
}
void PropertyBox::slotRemoveRowButtonClicked(BObject* object, const BValue& value)
{
	BTableItem* item = _attribTable->selectedItem();
	if (item)
	{
		_htmlNode->removeAttrib(item->text());
		_attribTable->removeRow(item->row());
		this->emit(Signal_ItemEdited, _htmlNode);
	}
}
void PropertyBox::slotEditButtonClicked(BObject* object, const BValue& value)
{
	const BString& fileName = object->asset();
	this->emit("edit-file", fileName);
}

void PropertyBox::slotTreeItemSelected(BObject* object, const BValue& value)
{
	BTreeItem* item = value;
	BXmlNode* node = item->asset();
	if (!item->selected())
		node = 0;
	if (_htmlNode != node)
	{
		_nameComboBox->clearItems();
		_formatEdit->setText(BString());
		_attribTable->clearItems();
		if (_htmlNode)
			disconnect(_htmlNode);
		_htmlNode = node;
		if (_htmlNode)
		{
			connect(_htmlNode, Signal_TextChanged, &PropertyBox::slotHtmlItemTextChanged);
			const BStyle* style = this->realStyle();
			while (node)
			{
				BIcon* icon = 0;
				if (node->name().empty())
					icon = style->icon("text");
				else
					icon = style->icon(node->name());
				_nameComboBox->addItem(icon, node->name(), node);
				node = node->parent();
			}
			_nameComboBox->setCurrentIndex(0);
			BString format = _htmlNode->format();
			if (format.size())
			{
				format.replace("\n", "\\n");
				_formatEdit->setText(format);
			}
			const BMap<BString, BString>& attribMap = _htmlNode->attribMap();
			_attribTable->setRowCount(attribMap.size());
			for (auto it = attribMap.begin(); it.valid(); ++it)
			{
				const BString& attribName = it.key();
				const BString& attribValue = it.value();
				_attribTable->setItem(it.index(), 0, attribName);
				_attribTable->setItem(it.index(), 1, attribValue);
				_attribTable->setRowLabel(it.index(), it.index());
			}
		}
	}
	if (BHtmlItem* item = _htmlNode.as<BHtmlItem>())
	{
		_htmlWidget->setItemSelected(item);
	}
}
void PropertyBox::slotTreeItemRightClicked(BObject* object, const BValue& value)
{
	if (BTreeItem* item = value)
	{
		const BPoint& pos = BCursor::GetPos(_treeWidget);
		_treeMenu->setPos(pos);
		_treeMenu->popup();
	}
}
void PropertyBox::slotTreeMenuTriggered(BObject* object, const BValue& value)
{
	if (BAction* action = value)
	{
		if (action->text() == "add")
		{

		}
		if (action->text() == "prepend")
		{

		}
		if (action->text() == "append")
		{

		}
		if (action->text() == "delete")
		{
			BTreeItemArray selection = _treeWidget->selection();
			for (int i = 0; i < selection.size(); i++)
			{
				BTreeItem* item = selection[i];
				BXmlNode* node = item->asset();
				node->parent()->removeChild(node);
				_treeWidget->removeItem(item);
			}
			this->emit(Signal_ItemEdited, _htmlNode);
		}
	}
}

void PropertyBox::styleEvent(const BEvent& event)
{
	const BStyle* style = this->realStyle();
	for (int i = 0; i < _treeWidget->itemCount(); i++)
	{
		BTreeItem* item = _treeWidget->item(i);
		freshTreeItemIcon(item);
	}
	_insertRowButton->setIcon(style->icon("insert-row"));
	_removeRowButton->setIcon(style->icon("remove-row"));
}
